今天理應照著昨天的技能延伸繼續做下去,但由於想到要做凍結或緩速的技能,就改生成速度和完善整個生成系統。
首先修改原本的TargetSpawner,加入生成間隔的調整設定。目前設定為每提高一個階級,也就是多一個目標時,就減少0.2秒的生成間隔,最短不會低於0.5秒。
另外,也修改了原本的生成方式。每次生成前會先檢查哪些洞口沒有目標,接著再從空洞口中隨機選擇一個生成,避免同一個洞口已經有目標時,又在同一個位置生成新的殭屍。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TargetSpawner : MonoBehaviour
{
[Header("生成設定")]
public Transform[] spawnPoints;
[Header("目標 Prefabs (按 1~5 滴血順序依序拖入)")]
public GameObject[] targetPrefabs;
[Header("時間與加速設定")]
public float baseSpawnInterval = 1.5f; // 初始生成間隔
public float intervalDecreasePerTier = 0.2f; // 每升一階減少的生成間隔時間 (秒)
public float minSpawnInterval = 0.5f;
[Header("殭屍移動與停留時間加速設定")]
public float speedIncreasePerTier = 0.15f; // 每升一階移動速度增加 15%
public float durationDecreasePerTier = 0.1f; // 每升一階停留時間減少 10%
private float currentSpawnInterval;
[Header("解鎖門檻設定")]
public int scoreForType2 = 1000;
public int scoreForType3 = 2500;
public int scoreForType4 = 5000;
public int scoreForType5 = 8500;
private void Start()
{
if (spawnPoints == null || spawnPoints.Length == 0 || targetPrefabs == null || targetPrefabs.Length == 0)
{
Debug.LogError("[TargetSpawner] 請確保已指派 spawnPoints 與 targetPrefabs!");
return;
}
currentSpawnInterval = baseSpawnInterval;
StartCoroutine(SpawnRoutine());
}
private IEnumerator SpawnRoutine()
{
while (true)
{
UpdateSpawnInterval();
yield return new WaitForSeconds(currentSpawnInterval);
SpawnTargetAtAvailableHole();
}
}
private void UpdateSpawnInterval()
{
int currentScore = ScoreManager.Instance != null ? ScoreManager.Instance.totalScore : 0;
int tier = GetCurrentTier(currentScore);
float calculatedInterval = baseSpawnInterval - (tier * intervalDecreasePerTier);
currentSpawnInterval = Mathf.Max(calculatedInterval, minSpawnInterval);
}
// 計算當前門檻階級 (0~4)
private int GetCurrentTier(int score)
{
if (score >= scoreForType5) return 4;
if (score >= scoreForType4) return 3;
if (score >= scoreForType3) return 2;
if (score >= scoreForType2) return 1;
return 0;
}
private void SpawnTargetAtAvailableHole()
{
// 1. 找出所有空洞口
List<Transform> emptyHoles = new List<Transform>();
foreach (Transform hole in spawnPoints)
{
if (hole.childCount == 0)
{
emptyHoles.Add(hole);
}
}
if (emptyHoles.Count == 0) return;
// 2. 取得當前階級與解鎖的殭屍種類
int currentScore = ScoreManager.Instance != null ? ScoreManager.Instance.totalScore : 0;
int tier = GetCurrentTier(currentScore);
int maxUnlockedIndex = Mathf.Min(tier, targetPrefabs.Length - 1);
// 3. 隨機選取殭屍 Prefab
int randomTargetIndex = Random.Range(0, maxUnlockedIndex + 1);
GameObject selectedTargetPrefab = targetPrefabs[randomTargetIndex];
// 4. 隨機選擇空洞口並生成
int randomHoleIndex = Random.Range(0, emptyHoles.Count);
Transform targetPoint = emptyHoles[randomHoleIndex];
GameObject newTarget = Instantiate(selectedTargetPrefab, targetPoint.position, Quaternion.identity, targetPoint);
// 5. 設定該隻殭屍的速度與停留時間加成
Target targetScript = newTarget.GetComponent<Target>();
if (targetScript != null)
{
float speedMultiplier = 1.0f + (tier * speedIncreasePerTier); // 例如第 2 階為 1.3 倍速
float durationMultiplier = Mathf.Max(0.3f, 1.0f - (tier * durationDecreasePerTier)); // 停留時間最短為 30%
targetScript.SetSpeedAndDuration(speedMultiplier, durationMultiplier);
}
}
}
這樣一來,生成器就會根據玩家目前的分數計算階級,再依照階級調整生成間隔與可以生成的殭屍種類。
除了生成間隔之外,這次也讓目標的移動速度與停留時間會隨著階級改變。每提高一個階級,移動速度就會增加15%,頂端停留時間則會減少10%,最短會保留原本停留時間的30%。
為了讓生成器能夠調整這些數值,我在Target中新增了SetSpeedAndDuration(),讓生成器在生成殭屍後,可以依照目前的階級設定移動速度與停留時間。
由於伸出與縮回都使用同一個speed,所以兩個動作也會一起加快。
using System.Collections;
using UnityEngine;
public class Target : MonoBehaviour
{
[Header("目標基本屬性")]
public int maxHp = 1;
protected int currentHp;
[Header("分數設定")]
public int scoreValue = 50;
[Header("移動與時間設定")]
public float riseDistance = 1.2f;
public float speed = 5f; // 預設移動速度(包含伸出與縮回)
public float showDuration = 1.0f; // 頂端停留時間
private Vector3 startPos;
private Vector3 targetPos;
private bool isDefeated = false;
private Coroutine targetRoutine;
// 動態根據階級調整速度與停留時間
public void SetSpeedAndDuration(float speedMultiplier, float durationMultiplier)
{
speed *= speedMultiplier; // 速度變快(乘以倍率,例如 1.2、1.4...)
showDuration *= durationMultiplier; // 停留時間變短(乘以倍率,例如 0.9、0.8...)
}
private void Start()
{
currentHp = maxHp;
startPos = transform.position;
targetPos = startPos + new Vector3(0, riseDistance, 0);
targetRoutine = StartCoroutine(TargetRoutine());
}
private IEnumerator TargetRoutine()
{
// 伸出洞口
while (Vector3.Distance(transform.position, targetPos) > 0.01f)
{
transform.position = Vector3.MoveTowards(transform.position, targetPos, speed * Time.deltaTime);
yield return null;
}
transform.position = targetPos;
// 在頂端停留
yield return new WaitForSeconds(showDuration);
// 縮回洞口(這裡使用的也是 speed,所以速度會同步提升!)
while (Vector3.Distance(transform.position, startPos) > 0.01f)
{
transform.position = Vector3.MoveTowards(transform.position, startPos, speed * Time.deltaTime);
yield return null;
}
transform.position = startPos;
if (HealthManager.Instance != null)
{
HealthManager.Instance.TakeDamage();
}
Destroy(gameObject);
}
public virtual void OnHit()
{
if (isDefeated) return;
currentHp--;
if (currentHp <= 0)
{
isDefeated = true;
if (targetRoutine != null)
{
StopCoroutine(targetRoutine);
}
if (ScoreManager.Instance != null)
{
ScoreManager.Instance.AddScore(scoreValue);
}
Destroy(gameObject);
}
}
}
今天主要完成了以下幾個部分: